﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EngineOfEvermore.Common
{
    public sealed class TryRenameEventArgs
    {
        bool _aborted;
        readonly String _newName;

        public TryRenameEventArgs( String newName )
        {
            _newName = newName;
        }

        public String NewName
        {
            get { return _newName; }
        }

        public bool HasBeenAborted
        {
            get { return _aborted; }
        }

        public void Abort()
        {
            _aborted = true;
        }
    }

    public interface IObjectWithOptionalName
    {
        event Action<TryRenameEventArgs> TryRenameObject;

        /// <summary>
        /// Get or set the name of the object. Name can be null.
        /// </summary>
        /// <remarks>
        /// When the name is changed in the setter, the setter must invoke the TryRenameObject
        /// event to determine if the name change is allowed. If the TryRenameObject event is null
        /// renaming is always allowed.
        /// </remarks>
        String Name { get; set; }
    }

    public class ObjectWithOptionalName : IObjectWithOptionalName
    {
        private String _name;

        public event Action<TryRenameEventArgs> TryRenameObject;

        public string Name
        {
            get { return _name; }
            set
            {
                if ( value != null && TryRenameObject != null )
                { 
                    TryRenameEventArgs e = new TryRenameEventArgs( value );
                    TryRenameObject( e );

                    if ( e.HasBeenAborted )
                    {
                        throw new ArgumentException( "An object with the name " + value + 
                            " is already present in the parent container." );
                    }
                }

                _name = value;
            }
        }
    }

    public class NamedObjectCollection<T> : IEnumerable<T> where T : IObjectWithOptionalName
    {
        #region Variables
        List<T> _items = new List<T>();
        #endregion

        #region Properties
        public int Count
        {
            get { return _items.Count; }
        }

        public T this[String name]
        {
            get
            {
                if ( name == null )
                {
                    throw new ArgumentNullException();
                }

                foreach ( T item in _items )
                {
                    if ( item.Name == name )
                    {
                        return item;
                    }
                }

                return default( T );
            }
        }
        #endregion

        #region Methods
        public void Add( T item )
        {
            if ( _items.Contains( item ) )
            {
                throw new ArgumentException( "VisualComponentChild already contained" );
            }

            if ( item.Name != null )
            {
                if ( _items.Any( x => item.Name == x.Name ) )
                {
                    throw new ArgumentException( "A VisualComponentChild with the name " +
                        item.Name + " already contained" );
                }
            }

            _items.Add( item );

            _onItemAdded( item );
        }

        protected virtual void _onItemAdded( T item )
        {
        }

        public bool Remove( T item )
        {
            if ( !_items.Contains( item ) )
            {
                return false;
            }

            _items.Remove( item );

            _onItemRemoved( item );

            return true;
        }

        protected virtual void _onItemRemoved( T item )
        {
        }

        public bool RemoveByKey( String name )
        {
            if ( name == null )
            {
                throw new ArgumentNullException();
            }

            T item = this[name];

            if ( item != null )
            {
                return this.Remove( item );
            }
            else
            {
                return false;
            }
        }

        public bool Contains( T item )
        {
            return _items.Contains( item );
        }

        public bool ContainsKey( String name )
        {
            if ( name == null )
            {
                throw new ArgumentNullException();
            }

            return _items.Any( item => item.Name == name );
        }

        public void Clear()
        {
            while ( _items.Count > 0 )
            {
                this.Remove( _items[_items.Count - 1] );
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _items.GetEnumerator();
        }
        #endregion
    }
}
